﻿using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using WeifenLuo.WinFormsUI.Docking;
using T3K.SketchEditor.Core;

namespace T3K.SketchEditor.Forms {
    public partial class ProjectExplorer : BaseForm {

        private Project _project = null;
        private ContextMenuStrip _folderMenu = null;
        private ContextMenuStrip _fileMenu = null;
        private ContextMenuStrip _projectMenu = null;
        private Image _folderItem_c = null;
        private Image _folderItem_o = null;
        private Image _projectItem = null;
        private Image _fileItem = null;

        public ContextMenuStrip FolderMenu {
            get { return _folderMenu; }
            set { _folderMenu = value; }
        }

        public ContextMenuStrip FileMenu {
            get { return _fileMenu; }
            set { _fileMenu = value; }
        }

        public ContextMenuStrip ProjectMenu {
            get { return _projectMenu; }
            set { _projectMenu = value; }
        }

        public ProjectExplorer() : base ("ProjectExplorer") {
            InitializeComponent();

            _folderItem_o = Editor.GetImage("Other/folder-open.png");
            _folderItem_c = Editor.GetImage("Other/folder.png");
            _projectItem = Editor.GetImage("Other/package-x-generic.png");
            _fileItem = Editor.GetImage("Other/text-x-generic.png");

            treeViewProject.LabelEdit = true;
            treeViewProject.DoubleClick += new EventHandler(treeViewProject_DoubleClick);
            treeViewProject.ImageList = new ImageList();
            treeViewProject.ImageList.Images.AddRange(
                new Image[] { _folderItem_o, _folderItem_c, _projectItem, _fileItem }
            );

            _folderMenu = new ContextMenuStrip();
            _fileMenu = new ContextMenuStrip();
            _projectMenu = new ContextMenuStrip();

            ToolStripMenuItem folder_FileNew = new ToolStripMenuItem("New File");
            ToolStripMenuItem folder_FileAdd = new ToolStripMenuItem("Add File");
            ToolStripMenuItem folder_FolderNew = new ToolStripMenuItem("New Folder");
            ToolStripMenuItem folder_FolderRemove = new ToolStripMenuItem("Remove");
            ToolStripMenuItem folder_FolderDelete = new ToolStripMenuItem("Delete");

            _folderMenu.Items.AddRange(new ToolStripItem[] { folder_FileNew, folder_FileAdd, folder_FolderNew, folder_FolderRemove, folder_FolderDelete });

            ToolStripMenuItem project_FileNew = new ToolStripMenuItem("New File");
            ToolStripMenuItem project_FileAdd = new ToolStripMenuItem("Add File");
            ToolStripMenuItem project_FolderNew = new ToolStripMenuItem("New Folder");

            _projectMenu.Items.AddRange(new ToolStripItem[] { project_FileNew, project_FileAdd, project_FolderNew });

            ToolStripMenuItem file_FileOpen = new ToolStripMenuItem("Open");
            ToolStripMenuItem file_FileRemove = new ToolStripMenuItem("Remove");
            ToolStripMenuItem file_FileDelete = new ToolStripMenuItem("Delete");

            _fileMenu.Items.AddRange(new ToolStripItem[] { file_FileOpen, file_FileRemove, file_FileDelete });

            folder_FileNew.Click += new EventHandler(FileNew_Click);
            project_FileNew.Click += new EventHandler(FileNew_Click);
            folder_FileAdd.Click += new EventHandler(FileAdd_Click);
            project_FileAdd.Click += new EventHandler(FileAdd_Click);
            folder_FolderNew.Click += new EventHandler(FolderNew_Click);
            project_FolderNew.Click += new EventHandler(FolderNew_Click);

            folder_FolderDelete.Click += new EventHandler(Delete_Click);
            file_FileDelete.Click += new EventHandler(Delete_Click);
            folder_FolderRemove.Click += new EventHandler(Remove_Click);
            file_FileRemove.Click += new EventHandler(Remove_Click);

            file_FileOpen.Click += new EventHandler(file_FileOpen_Click);

            this.LoadProject(Editor.GetVariable("Project") as Project);

        }

        void treeViewProject_DoubleClick(object sender, EventArgs e) {
            TreeNode node = treeViewProject.SelectedNode;
            Project.ProjectNode pNode = node.Tag as Project.ProjectNode;
            if (pNode != null) {
                switch (pNode.NodeType) {
                    case Project.ProjectNodeType.CodeFile:
                    case Project.ProjectNodeType.HeaderFile:
                        file_FileOpen_Click(this, new EventArgs());
                        break;
                    default:
                        break;
                }
            }
        }

        void file_FileOpen_Click(object sender, EventArgs e) {
            TreeNode node = treeViewProject.SelectedNode;

            if (node != null) {

                Project.ProjectNode pNode = node.Tag as Project.ProjectNode;

                if (pNode == null) {
                    Project p = node.Tag as Project;
                    if (p != null)
                        pNode = p.RootNode;
                }

                if (pNode != null) {
                    CommandArgs args = new CommandArgs();
                    args["FileName"] = pNode.FilePath;
                    Editor.InvokeCommand(this, "Main.OpenFile", args);
                }
            }
        }

        private void treeViewProject_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) {
            treeViewProject.SelectedNode = e.Node;
        }

        private void treeViewProject_AfterSelect(object sender, TreeViewEventArgs e) {
            TreeNode node = e.Node;
            if (node != null)
                propertyGridDetails.SelectedObject = node.Tag;            
        }

        private void treeViewProject_AfterLabelEdit(object sender, NodeLabelEditEventArgs e) {

            string newFileName = e.Label;
            Project.ProjectNode pNode = e.Node.Tag as Project.ProjectNode;

            if (newFileName == null)
                return;

            if (pNode != null) {
                FileInfo currFInfo = new FileInfo(pNode.FilePath);
                DirectoryInfo currDInfo = new DirectoryInfo(pNode.FilePath);
                if (currFInfo.Exists) {
                    FileInfo newFInfo = new FileInfo(Path.Combine(currFInfo.Directory.FullName, newFileName));
                    if (!newFInfo.Exists) {
                        currFInfo.MoveTo(newFInfo.FullName);
                        pNode.Name = newFInfo.Name;

                        Project.ProjectNodeType nodeType = pNode.NodeType;
                        switch (newFInfo.Extension.ToLower()) {
                            case ".h":
                                nodeType = Project.ProjectNodeType.HeaderFile; break;
                            case ".cpp":
                            case ".c":
                                nodeType = Project.ProjectNodeType.CodeFile; break;
                        }
                        pNode.NodeType = nodeType;
                        _project.Save();
                    }
                } else if (currDInfo.Exists) {
                    DirectoryInfo newDInfo = new DirectoryInfo(Path.Combine(currDInfo.Parent.FullName, newFileName));
                    if (!newDInfo.Exists) {
                        currDInfo.MoveTo(newDInfo.FullName);
                        pNode.Name = newDInfo.Name;
                        _project.Save();
                    }
                } else {
                    FileSystemInfo fsInfo;
                    switch (pNode.NodeType) {
                        case Project.ProjectNodeType.Folder:
                            fsInfo = new DirectoryInfo(Path.Combine(currFInfo.Directory.FullName, newFileName));
                            break;
                        default:
                            fsInfo = new FileInfo(Path.Combine(currFInfo.Directory.FullName, newFileName));
                            break;
                    }

                    if (!fsInfo.Exists) {
                        FileInfo fInfo = fsInfo as FileInfo;
                        DirectoryInfo dInfo = fsInfo as DirectoryInfo;

                        if (fInfo != null)
                            fInfo.Create();

                        if (dInfo != null)
                            dInfo.Create();

                        _project.Save();
                    }
                }
            }
        }

        void Remove_Click(object sender, EventArgs e) {
            throw new NotImplementedException();
        }

        void Delete_Click(object sender, EventArgs e) {
            throw new NotImplementedException();
        }

        void FolderNew_Click(object sender, EventArgs e) {
            TreeNode node = treeViewProject.SelectedNode;
            if (node != null) {
                Project.ProjectNode pNode = node.Tag as Project.ProjectNode;

                if (pNode == null) {
                    Project p = node.Tag as Project;
                    if (p != null)
                        pNode = p.RootNode;
                }

                if (pNode != null) {

                    Project.ProjectNode newFolder = new Project.ProjectNode(
                        "New Folder", Project.ProjectNodeType.Folder, _project.ProjectDir, pNode);

                    TreeNode newTNode = new TreeNode(newFolder.Name);

                    DirectoryInfo dInfo = new DirectoryInfo(newFolder.FilePath);

                    if (!dInfo.Exists) {
                        dInfo.Create();
                        newTNode.Tag = newFolder;
                        newTNode.ContextMenuStrip = _folderMenu;
                        node.Nodes.Add(newTNode);
                        pNode.Nodes.Add(newFolder);
                        newTNode.BeginEdit();
                    } else {
                        MessageBox.Show(
                            string.Format("Directory {0} already exists", dInfo.Name),
                            "Directory exists.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    }
                }
            }
        }

        void FileAdd_Click(object sender, EventArgs e) {

            TreeNode node = treeViewProject.SelectedNode;
            if (node != null) {
                Project.ProjectNode pNode = node.Tag as Project.ProjectNode;

                if (pNode == null) {
                    Project p = node.Tag as Project;
                    if (p != null)
                        pNode = p.RootNode;
                }

                if (pNode != null) {

                    string basePath = (pNode.NodeType == Project.ProjectNodeType.Folder) ?
                        pNode.FilePath : (new FileInfo(pNode.FilePath)).Directory.FullName;

                    OpenFileDialog openfile = new OpenFileDialog();
                    openfile.Filter = "c++ code file (*.cpp) | *.cpp; c code file (*.c) | *.c; header file (*.h) | *.h";
                    openfile.CheckFileExists = true;
                    openfile.CheckPathExists = true;
                    openfile.Multiselect = true;

                    if (openfile.ShowDialog() == DialogResult.OK) {

                        for (int i = 0; i < openfile.FileNames.Length; i++) {
                            FileInfo fInfo = new FileInfo(openfile.FileNames[i]);
                            if (fInfo.Exists) {
                                string filePath = Path.Combine(basePath, fInfo.Name);

                                if (!File.Exists(filePath))
                                    fInfo.CopyTo(filePath);
                                else {
                                    if (MessageBox.Show(
                                        string.Format("The file {0} already exists. do you which to replace it?", fInfo.Name),
                                        "File exists.", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2
                                        ) == DialogResult.Yes
                                    ) {
                                        fInfo.CopyTo(filePath, true);
                                    }  else
                                        return;
                                }

                                Project.ProjectNodeType nodeType = Project.ProjectNodeType.Other;

                                switch (fInfo.Extension.ToLower()) {
                                    case ".h": 
                                        nodeType = Project.ProjectNodeType.HeaderFile; break; 
                                    case ".cpp": 
                                    case ".c":
                                        nodeType = Project.ProjectNodeType.CodeFile; break;
                                }

                                Project.ProjectNode newNode = new Project.ProjectNode(
                                    fInfo.Name, nodeType, _project.ProjectDir, pNode);

                                pNode.Nodes.Add(newNode);
                            }
                        }
                    }
                }
            }

            _project.Save();
        }

        void FileNew_Click(object sender, EventArgs e) {
            NewFileForm form = new NewFileForm();
            if (form.ShowDialog() == DialogResult.OK) {

                string fileName = form.FileName;
                string templateName = form.Template.TemplateName;

                TreeNode node = treeViewProject.SelectedNode;
                if (node != null) {

                    Project.ProjectNode pNode = node.Tag as Project.ProjectNode;

                    if (pNode == null) {
                        Project p = node.Tag as Project;
                        if (p != null)
                            pNode = p.RootNode;
                    }

                    if (pNode != null) {

                        DirectoryInfo dInfo;

                        switch (pNode.NodeType) {
                            case Project.ProjectNodeType.Folder:
                                dInfo = new DirectoryInfo(pNode.FilePath);
                                break;
                            default:
                                dInfo = new FileInfo(pNode.FilePath).Directory;
                                break;
                        }

                        if (dInfo.Exists) {

                            string nodeName = fileName;
                            fileName = Path.Combine(dInfo.FullName, fileName);

                            FileInfo fInfo = new FileInfo(fileName);

                            if (fInfo.Exists) {
                                MessageBox.Show(
                                    string.Format("The file {0} already exists!", fInfo.Name),
                                    "File exists.", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1
                                );
                                return;
                            }

                            Project.ProjectNodeType nodeType = Project.ProjectNodeType.Other;

                            switch (fInfo.Extension.ToLower()) {
                                case ".h":
                                    nodeType = Project.ProjectNodeType.HeaderFile; break;
                                case ".cpp":
                                case ".c":
                                    nodeType = Project.ProjectNodeType.CodeFile; break;
                            }

                            Project.ProjectNode newNode = new Project.ProjectNode(
                                nodeName, nodeType, _project.ProjectDir, pNode);

                            pNode.Nodes.Add(newNode);

                            _project.CreateFileFromTemplate(fInfo.FullName, templateName, new Dictionary<string,object>());
                            _project.Save();
                            RefreshProject();

                        }
                    }
                }
            }
        }

        [Command("LoadProject", 
            Desc = "Loads a new project into the project explorer window", 
            ParamsDesc = "project as Project")]
        public void Command_LoadProject(object sender, CommandArgs args) {
            Project project = args["Project"] as Project;
            if (project != null)
                LoadProject(project);
        }

        public void LoadProject(Project project) {

            _project = project;
            treeViewProject.SuspendLayout();
            treeViewProject.Nodes.Clear();

            if (_project != null) {
                TreeNode node = new TreeNode(_project.ProjectName);
                node.ImageIndex = 2;
                node.SelectedImageIndex = 2;
                node.ContextMenuStrip = _projectMenu;
                node.Tag = _project;

                BuildTree(node, project.RootNode);
                treeViewProject.Nodes.Add(node);

                treeViewProject.ResumeLayout(false);
            }
        }

        [Command("RefreshProject", Desc = "refresh the current project in the project explorer window")]
        public void Command_RefreshProject(object sender, CommandArgs args) {
            RefreshProject();
        }

        public void RefreshProject() {
            if (_project != null) {
                treeViewProject.SuspendLayout();
                treeViewProject.Nodes.Clear();

                TreeNode node = new TreeNode(_project.ProjectName);
                node.ImageIndex = 2;
                node.SelectedImageIndex = 2;
                node.ContextMenuStrip = _projectMenu;
                node.Tag = _project;

                BuildTree(node, _project.RootNode);
                treeViewProject.Nodes.Add(node);

                treeViewProject.ResumeLayout(false);
            }
        }

        private void BuildTree(TreeNode treeNode, Project.ProjectNode projectNode) {
            for (int i = 0; i < projectNode.Nodes.Count; i++) {
                TreeNode newNode = new TreeNode(projectNode.Nodes[i].Name);

                switch (projectNode.Nodes[i].NodeType) {
                    case Project.ProjectNodeType.CodeFile:
                    case Project.ProjectNodeType.HeaderFile:
                        newNode.ContextMenuStrip = _fileMenu;
                        newNode.ImageIndex = 3;
                        newNode.SelectedImageIndex = 3;
                        break;

                    case Project.ProjectNodeType.Folder: 
                        newNode.ContextMenuStrip = _folderMenu;
                        newNode.ImageIndex = 1;
                        newNode.SelectedImageIndex = 0;
                        break;

                    case Project.ProjectNodeType.Project: 
                        newNode.ContextMenuStrip = _projectMenu;
                        newNode.ImageIndex = 2;
                        newNode.SelectedImageIndex = 2;
                        break;

                    default: break;
                }

                newNode.Tag = projectNode.Nodes[i];
                treeNode.Nodes.Add(newNode);

                BuildTree(newNode, projectNode.Nodes[i]);
            }
        }



    }
}
